using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace WPCoco.Graphics
{
    public class QuadStream
    {
        public QuadStream()
        {
            MaxSize = 10000;
            CreateBasicEffect();
        }

        #region BasicEffect

        RasterizerState desiredRasterizerState;
        BlendState desiredBlendState;

        RasterizerState lastRasterizerState;
        BlendState lastBlendState;

        BasicEffect basicEffect;

        void CreateBasicEffect()
        {
            desiredRasterizerState = new RasterizerState();
            desiredRasterizerState.CullMode = CullMode.None;
            desiredRasterizerState.MultiSampleAntiAlias = false;

            desiredBlendState = BlendState.AlphaBlend;

            basicEffect = new BasicEffect(Screen.CurrentGraphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.TextureEnabled = true;
            
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, Screen.Width,     // left, right
                                                                        Screen.Height, 0,    // bottom, top
                                                                        zNearPlane, zFarPlane);
            basicEffect.World = Matrix.Identity;
        }

        void UpdateBasicEffect() 
        {

            //basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, Screen.Width,    
            //                                                            Screen.Height, 0,   
            //                                                            zNearPlane, zFarPlane);

            //basicEffect.Projection = Matrix.CreatePerspectiveOffCenter(0, Screen.Width,
            //                                                           Screen.Height, 0,
            //                                                           1, 10000);

            basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, (float)Screen.Width / (float)Screen.Height, 1, 200);

        }

        public Matrix BaseWorldMatrix
        {
            get { return basicEffect.World; }
            set { basicEffect.World = value; }
        }

        public Texture2D Texture
        {
            get { return basicEffect.Texture; }
            set { basicEffect.Texture = value; }
        }

        float zNearPlane = -1.0f;

        public float ZNearPlane
        {
            get { return zNearPlane; }
            set { zNearPlane = value; }
        }

        float zFarPlane = 1.0f;

        public float ZFarPlane
        {
            get { return zFarPlane; }
            set { zFarPlane = value; }
        }

        #endregion

        public int MaxSize { get; set; }

        VertexPositionColorTexture[] quads = null; 

        void EnsureQuadBufferCapacity() 
        {
            if (MaxSize < 1) throw new Exception("MaxSize must be greater than 0");

            if (quads == null || quads.Length < MaxSize)
                quads = new VertexPositionColorTexture[MaxSize * 6];
        }

        int usedQuads = 0;

        public void Begin() 
        {
            EnsureQuadBufferCapacity();
            UpdateBasicEffect();

            usedQuads = 0;

            lastRasterizerState = Screen.CurrentGraphicsDevice.RasterizerState;
            lastBlendState = Screen.CurrentGraphicsDevice.BlendState;
            Screen.CurrentGraphicsDevice.RasterizerState = desiredRasterizerState;
            Screen.CurrentGraphicsDevice.BlendState = desiredBlendState;

            basicEffect.Texture = Texture;

            Screen.CurrentGraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
        }

        #region Quads Builders

        public void Add(AtlasQuad quad) 
        {
            int index = usedQuads * 6;
            quads[index + 0] = quad.vertexs[0];
            quads[index + 1] = quad.vertexs[1];
            quads[index + 2] = quad.vertexs[2];
            quads[index + 3] = quad.vertexs[0];
            quads[index + 4] = quad.vertexs[2];
            quads[index + 5] = quad.vertexs[3];
            usedQuads++;
            if (usedQuads == MaxSize) 
                Flush();
        }

        public void Add(TextureFrame frame, RectangleF location, Color color, float depth) 
        {
            Add(AtlasQuad.Create(frame, location, color, depth));
        }

        #endregion

        public void Flush() 
        {
            if (usedQuads == 0) return;
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList,
                                                                                            quads,
                                                                                            0,
                                                                                            usedQuads * 2);
            }
            usedQuads = 0;
        }

        public void End() 
        {
            Flush();
            Screen.CurrentGraphicsDevice.RasterizerState = lastRasterizerState;
            Screen.CurrentGraphicsDevice.BlendState = lastBlendState;
        }
    }
}
